#! /usr/bin/python

# To change this template, choose Tools | Templates
# and open the template in the editor.

__author__="lustalov"
__date__ ="$Jan 10, 2010 11:27:21 AM$"

import unittest as ut
import basicVdsTest as vdst
import sanity_mate as sm
from confUtils import mergeTemplates
import vdsClient as vdsc
import random
import time, re
import qaDB as qadb

SYSTEM_DISK = 1
DATA_DISK   = 2

volumeType_e = {
    vdsc.SPARSE_VOL       : 'SPARSE',
    vdsc.PREALLOCATED_VOL : 'PREALLOCATED',
    vdsc.COW_FORMAT       : 'COW',
    vdsc.RAW_FORMAT       : 'RAW'
}

MB                   = 1048576
GB                   = MB * 1024
VOL_SIZE_MIN  = 1 * GB
VOL_SIZE_MAX  = 1000 * GB

IMAGE_COPY = 1
IMAGE_MOVE = 2
imageOp_e = {
    IMAGE_COPY : 'COPY',
    IMAGE_MOVE : 'MOVE'
}

#-----------------------------------------------------------------------------#
class volumeTest(vdst.BasicVDSTest):

    @classmethod
    def getConfigTemplate(cls):
        return mergeTemplates([sm.__commonTemplate__, sm.__nfsTemplate__,
            sm.__iscsiTemplate__, sm.__fcpTemplate__])

    def setUp(self):
        cfg = self.cfg['common']
        self.testDebug = int(cfg['testDebug'])
        self.dcNamePref = cfg['dcNamePref']
        self.vds1 = cfg['vds1']
        self.storageTypeName = cfg['storageType']
        self.storageType = int(sm.storageTypes_e[self.storageTypeName])
        self.logFile = "/tmp/sanity.log"
        self.doSPremove = False

        self.iso = self.cfg['iso']
        self.backupStorageType = None
        self.backupStorage = {}
        for opt in self.cfg['backup']:
            if opt == 'type':
                type = self.cfg['backup'][opt]
                self.backupStorageType = int(sm.storageTypes_e[type])
            else:
                self.backupStorage[opt] = self.cfg['backup'][opt]

        self.storageParams = {}
        self.storageParams[self.storageType] = []
        for section in self.cfg.keys():
            if re.match(self.storageTypeName, section, re.I):
                conn = {}
                for opt in self.cfg[section]:
                    conn[opt] = self.cfg[section][opt]
                self.storageParams[self.storageType].append(conn)

        volOpt = {
                vdsc.COW_FORMAT : [SYSTEM_DISK, DATA_DISK],
                vdsc.RAW_FORMAT : [SYSTEM_DISK, DATA_DISK]
        }
        self.volType = {
               vdsc.SPARSE_VOL       :  volOpt,
               vdsc.PREALLOCATED_VOL :  volOpt
        }
        if self.storageType != sm.STORAGE_TYPE_NFS:
            del(self.volType[vdsc.SPARSE_VOL][vdsc.RAW_FORMAT])

        vdst.BasicVDSTest.setUp(self)
        self.common = sm.VDSClient_accessory(self.logFile)
        self.common.setUp(self.vds1)

        self.qadb = qadb.DBcommunicator(self.logFile)
        self.testStatus = True
        isQaDB = int(cfg['qadb'])
        if isQaDB == 1:
            qaSetupName = cfg['qaSetup']
            buildName = cfg['buildName']
            vdsCap = self.common.getVdsCap()
            self.qadb.initDBParams(self.storageTypeName, buildName,
                vdsCap['software_version'], vdsCap['software_revision'],
                qaSetupName)

        if self.testDebug == 2:
            import pdb
            pdb.set_trace()

        self.createStoragePool()

    def tearDown(self):
        self.removeAllImages()
        self.removeStoragePool()
        vdst.BasicVDSTest.tearDown(self)

    def __log__(self, msg, rc = True, dbMsg = 'info'):
        sm.writeLog(msg, self.logFile, self.testDebug)
        if not rc:
            self.testStatus = False
        if dbMsg == 'start':
            self.qadb.testStart(msg)
        elif dbMsg == 'info':
            self.qadb.testPrintInfo(rc, msg)

    def assertCallAndLog(self, msg, method, *args):
        fun = getattr(self.common, method)
        rc = fun(*args)
        self.__log__(msg, rc)
        self.assertTrue(rc, "ERROR in: %s" % msg)

    def assertVdsCallAndLog(self, msg, method, *args, **kwargs):
        fun = getattr(self.common, method)
        resp = fun(*args, **kwargs)
        self.__log__(msg, resp['status']['code'] == 0)
        return self.assertVdscOK((resp))

    def assertEqualAndLog(self, var1, var2, msg, errorMsg):
        isEqual = (var1 == var2)
        self.__log__(msg, isEqual)
        self.assertTrue(isEqual, errorMsg)


    @classmethod
    def tests(cls):
        return ([
                "testVolumeCreate",
                "testVolumePath",
                "testVolumeDescription",
                "testVolumeLegality",
                "testVolumeSnapshot",
                "testVolumeGetList",
                "testVolumeRemove",
                "testVolumeSizeLimit",
                "testVolumeExtend",
                "testSnapshotMerge",
                "testVolumeCopy",
                "testVolumeExportImport",
                "dummyCleanup"
                ])

    def createStoragePool(self):
        spName = self.dcNamePref + self.storageTypeName
        self.spUuid = self.common.getPoolUuidByName(spName)
        if not self.spUuid:
            self.__log__("volumeTests_" + self.storageTypeName, dbMsg = 'start')
            self.assertCallAndLog("CreatStoragePool_" + self.storageTypeName,
                "createSP", self.storageType, spName,
                self.storageParams[self.storageType], self.iso)
            self.spUuid = self.common.getPoolUuidByName(spName)
        self.sdUuid = self.common.getPoolMasterDomain(self.spUuid)

    def removeStoragePool(self):
        if self.doSPremove:
            spName = self.dcNamePref + self.storageTypeName
            self.assertCallAndLog("DestroyStoragePool", "removeSP", self.storageType,
                spName, self.storageParams[self.storageType], self.iso)

    def _createVolume(self, allocateType = vdsc.SPARSE_VOL, volFormat = vdsc.COW_FORMAT,
                    diskType = SYSTEM_DISK, size = 0, description = 'testvolume',
                    failureExp = False, timeout = 600):
        imgGuid = self.common.newUUID()
        volGuid = self.common.newUUID()
        if not size:
            maxSize = None
            if allocateType == vdsc.PREALLOCATED_VOL:
                maxSize = 2 * GB
            size = self.getRandomVolumeSize(maxSize)
        text = "Create %s volume: volFormat = %s, diskType = %d, size = %d" % (
            volumeType_e[allocateType], volumeType_e[volFormat], diskType, size)
        if not failureExp:
            self.assertVdsCallAndLog(text, "createVolume", self.sdUuid, self.spUuid,
                imgGuid, size, volFormat, allocateType, diskType, volGuid,
                    description, timeout=timeout)
        else:
            self.assertRaises(vdsc.TaskFailed, self.common.createVolume,
                    self.sdUuid, self.spUuid, imgGuid, size, volFormat,
                    allocateType, diskType, volGuid, description, timeout=timeout)
        return imgGuid, volGuid

    def _createSnapshot(self, imgUuid, volUuid, allocateType = vdsc.SPARSE_VOL,
                    volFormat = vdsc.COW_FORMAT, diskType = SYSTEM_DISK,
                    size = 0, description = 'testsnap', timeout = 600):
        snapUuid = self.common.newUUID()
        self.assertVdsCallAndLog("Create snapshot", "createVolume",
            self.sdUuid, self.spUuid, imgUuid, size, volFormat, allocateType,
            diskType, snapUuid, description, imgUuid, volUuid, timeout=timeout)
        return snapUuid

    def _copyImage(self, imgUuid, volUuid, allocateType = vdsc.SPARSE_VOL,
                    volFormat = vdsc.COW_FORMAT, timeout = 600):
        vmUuid = self.common.newUUID()
        newImgUuid = self.common.newUUID()
        newVolUuid = self.common.newUUID()
        text = "Copy image: destVolType = %s, destVolFormat = %s" % (
            volumeType_e[allocateType], volumeType_e[volFormat])
        self.assertVdsCallAndLog(text, "copyImage", self.sdUuid, self.spUuid,
            vmUuid, imgUuid, volUuid, newImgUuid, newVolUuid,
            volFormat=volFormat, preallocate=allocateType, timeout=timeout)
        return newImgUuid, newVolUuid

    def _moveImage(self, destSdUuid, imgUuid, opCode = IMAGE_COPY, timeout = 600):
        vmUuid = self.common.newUUID()
        text = "%s image to storage domain %s" % (imageOp_e[opCode], destSdUuid)
        self.assertVdsCallAndLog(text, "moveImage", self.spUuid, self.sdUuid,
            destSdUuid, imgUuid, vmUuid, opCode, timeout=timeout)
        return imgUuid

    def _getVolInfo(self, sdUuid, spUuid, imgGuid, volGuid):
        msg = self.assertVdsCallAndLog("Get Volume Info", "getVolumeInfo",
            sdUuid, spUuid, imgGuid, volGuid)
        volInfo = {}
	for element in msg['info'].keys():
            volInfo[element] = msg['info'][element]
        return volInfo

    def getVolParent(self, sdUuid, spUuid, imgGuid, volGuid):
        volInfo = self._getVolInfo(sdUuid, spUuid, imgGuid, volGuid)
        return volInfo['parent']

    def getRandomVolumeSize(self, maxVolSize = None):
        if not maxVolSize:
            maxVolSize = VOL_SIZE_MAX
            if self.storageType != sm.STORAGE_TYPE_NFS:
                maxVolSize = 80 * GB    #TBD up to VG size
        return random.randrange(VOL_SIZE_MIN, maxVolSize, 10 * GB)

    def removeAllImages(self):
        msg = self.assertVdsCallAndLog("Get Images List", "getImagesList",self.sdUuid)
        for image in msg['imageslist']:
            self.assertVdsCallAndLog("Delete Image", "deleteImage",
                self.sdUuid, self.spUuid, image)

    def getDomainCapacity(self, sdUuid):
        msg = self.assertVdscOK(self.common.getStoragePoolInfo(self.spUuid))
        sdCapacity = msg['dominfo'][sdUuid]['diskfree'] #in GB
        return int(sdCapacity)

    def testVolumeCreate(self):
        for allocateType, volOpt in self.volType.iteritems():
            for volFormat, diskTypes in volOpt.iteritems():
                for diskType in diskTypes:
                    imgGuid, volGuid = self._createVolume(allocateType = allocateType,
                                        volFormat = volFormat, diskType = diskType)

                    #check volume info
                    volInfo = self._getVolInfo(self.sdUuid, self.spUuid, imgGuid, volGuid)
                    checkKeys = {
                                'type'        : volumeType_e[allocateType],
                                'format'      : volumeType_e[volFormat],
                                'disktype'    : str(diskType),
                                'voltype'     : 'LEAF'
                    }
                    for key in checkKeys.keys():
                        self.assertEqualAndLog(volInfo[key], checkKeys[key],
                            "Check volume %s"%key, "Volume %s is wrong: %s!" % (key, str(volInfo[key])))

                    #delete volume
                    self.assertVdsCallAndLog("Delete Image", "deleteImage",
                        self.sdUuid, self.spUuid, imgGuid)

    def testVolumePath(self):
        imgGuid, volGuid = self._createVolume()

        msg = self.assertVdsCallAndLog("Get volume path", "getVolumePath",
            self.sdUuid, self.spUuid, imgGuid, volGuid)
        volPath = '/rhev/data-center/' + self.spUuid + '/' + self.sdUuid + '/images/' + imgGuid + '/' + volGuid
        self.assertEqualAndLog(msg['path'].strip('\n'), volPath, "Check volume path",
            "Volume path is wrong: %s" % msg)

    def testVolumeDescription(self):
        descr = "someInitialDescription"
        imgGuid, volGuid = self._createVolume(description = descr)

        #check volume description
        volInfo = self._getVolInfo(self.sdUuid, self.spUuid, imgGuid, volGuid)
        self.assertEqualAndLog(volInfo['description'], descr, "Check volume description",
                        "Volume description is wrong: %s!" % volInfo['description'])

        #set and check the new description
        newDescr = "someNewDescription"
        self.assertVdsCallAndLog("Set volume description", "setVolumeDescription",
            self.sdUuid, self.spUuid, imgGuid, volGuid, newDescr)
        volInfo = self._getVolInfo(self.sdUuid, self.spUuid, imgGuid, volGuid)
        self.assertEqualAndLog(volInfo['description'], newDescr, "Check volume description",
                        "Volume description is wrong: %s!" % volInfo['description'])

    def testVolumeLegality(self):
        imgGuid, volGuid = self._createVolume()

	for legality in "ILLEGAL", "LEGAL":
            self.assertVdsCallAndLog("Set volume legality to %s" % legality, "setVolumeLegality",
                self.sdUuid, self.spUuid, imgGuid, volGuid, legality)
            volInfo = self._getVolInfo(self.sdUuid, self.spUuid, imgGuid, volGuid)
            self.assertEqualAndLog(volInfo['legality'], legality, "Check volume legality",
                            "Volume %s legality is wrong: %s" % (volGuid, volInfo['legality']))

    def testVolumeSnapshot(self):
        volSize = self.getRandomVolumeSize()
        imgGuid, volGuid = self._createVolume(size = volSize)

        snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)

        parentUuid = self.getVolParent(self.sdUuid, self.spUuid, imgGuid, snapUuid)
        self.assertEqualAndLog(parentUuid, volGuid, "Check volume parent",
                        "Volume %s parent is wrong: %s" % (volGuid, parentUuid))

    def testVolumeGetList(self):
        volCnt = 2
        snapCnt = 2
        snapListAll = []
        imgList = []
        for volInd in range(0, volCnt):
            volSize = self.getRandomVolumeSize()

            imgGuid, volGuid = self._createVolume(size = volSize)
            imgList.append(imgGuid)
            snapList = []

            for i in range(0, snapCnt):
                snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)
                snapList.append(snapUuid)
                snapListAll.append(snapUuid)
                time.sleep(1)

            msg = self.assertVdsCallAndLog("Get volumes list", "getVolumesList",
                self.sdUuid, self.spUuid, imgGuid)
            self.assertEqualAndLog(len(msg['uuidlist']), snapCnt+1,
                "Check volume list of image", "Volume list length of %s is wrong!" % imgGuid)
            for sn_src in snapList:
                self.assertTrue(sn_src in msg['uuidlist'], "Volume %s is not found in list!" % sn_src)

        msg = self.assertVdsCallAndLog("Get images list", "getImagesList",self.sdUuid)
        self.assertEqualAndLog(len(msg['imageslist']), volCnt,
            "Check image list of storage domain", "Image list length is wrong!")
        for im_src in imgList:
            self.assertTrue(im_src in msg['imageslist'], "Image %s is not found in list!" % im_src)

    def testVolumeRemove(self):
        volSize = self.getRandomVolumeSize()
        imgGuid, volGuid = self._createVolume(size = volSize)

        snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)

        self.assertVdsCallAndLog("Delete snapshot", "deleteVolume",
            self.sdUuid, self.spUuid, imgGuid, [snapUuid])
        self.assertVdsCallAndLog("Delete volume", "deleteVolume",
            self.sdUuid, self.spUuid, imgGuid, [volGuid])
        self.assertVdsCallAndLog("Delete Image", "deleteImage",
            self.sdUuid, self.spUuid, imgGuid)

    def testVolumeSizeLimit(self):
        pass

    def testVolumeExtend(self):
        if self.storageType == sm.STORAGE_TYPE_NFS:
            pass
        else:
            volSize = self.getRandomVolumeSize()
            imgGuid, volGuid = self._createVolume(size = volSize)

            snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)
            free = self.getDomainCapacity(self.sdUuid)
            newSize = (volSize + 10 * GB) / MB
            if newSize > free / MB:
                newSize = free / MB
            self.assertVdsCallAndLog("Extend Volume", "extendVolume",
                self.sdUuid, self.spUuid, imgGuid, snapUuid, newSize)
            volInfo = self._getVolInfo(self.sdUuid, self.spUuid, imgGuid, snapUuid)
            self.assertEqualAndLog(int(volInfo['truesize'].strip('\n')), newSize * MB,
                "Check volume size", "Volume %s size is wrong: %s" % (snapUuid, volInfo['size']))

    def testSnapshotMerge(self):
        volSize = self.getRandomVolumeSize()
        imgGuid, volGuid = self._createVolume(size = volSize)

        #create snapshot
        snapUuid1 = self._createSnapshot(imgGuid, volGuid, size = volSize)
        #extend volume
        free = self.getDomainCapacity(self.sdUuid)
        newSize = (volSize + 10 * GB) / MB
        if newSize > free / MB:
            newSize = free / MB / 2
        self.assertVdsCallAndLog("Extend Volume", "extendVolume",
            self.sdUuid, self.spUuid, imgGuid, volGuid, newSize)
        #create the second snapshot
        snapUuid2 = self._createSnapshot(imgGuid, snapUuid1, size = newSize)

        #merge snapshots (delete the first)
        vmUuid = self.common.newUUID()
        self.assertVdsCallAndLog("Merge snapshots", "mergeSnapshots",
            self.sdUuid, self.spUuid, vmUuid, imgGuid, volGuid, snapUuid1)

        #create one more snapshot
        snapUuid3 = self._createSnapshot(imgGuid, snapUuid2, size = newSize)
        #extend volume
        free = self.getDomainCapacity(self.sdUuid)
        newSize = (volSize + 5 * GB) / MB
        if newSize > free / MB:
            newSize = free / MB
        self.assertVdsCallAndLog("Extend Volume", "extendVolume",
            self.sdUuid, self.spUuid, imgGuid, snapUuid3, newSize)

        #merge snapshots (delete the second)
        self.assertVdsCallAndLog("Merge snapshots", "mergeSnapshots",
            self.sdUuid, self.spUuid, vmUuid, imgGuid, snapUuid1, snapUuid2)

    def testVolumeCopy(self):
        for allocType, volFormat in self.volType.iteritems():
            for format in volFormat.keys():
                maxSize = 2 * GB
                volSize = self.getRandomVolumeSize(maxSize)
                imgGuid, volGuid = self._createVolume(size = volSize, allocateType = allocType,
                            volFormat = format)

                snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)

                #copy image (rhevm: create new template from VM)
                destIm, destVol = self._copyImage(imgGuid, snapUuid)
                time.sleep(1)

                #delete the copied image
                self.assertVdsCallAndLog("Delete destination image", "deleteImage",
                    self.sdUuid, self.spUuid, destIm)

                self.assertVdsCallAndLog("Delete source image", "deleteImage",
                    self.sdUuid, self.spUuid, imgGuid)

    def testVolumeExportImport(self):
        sdName = self.dcNamePref + "export"
        self.assertCallAndLog("AddExportDomainToStoragePool", "addDomainToStoragePool",
            self.spUuid, self.backupStorageType,
            sm.BACKUP_DOMAIN, sdName, self.backupStorage)
        expSdUuid = self.common.getPoolExportDomain(self.spUuid)
        for allocType, volFormat in self.volType.iteritems():
            for format in volFormat.keys():
                maxSize = 2 * GB
                volSize = self.getRandomVolumeSize(maxSize)
                imgGuid, volGuid = self._createVolume(size = volSize, allocateType = allocType,
                            volFormat = format)

                snapUuid = self._createSnapshot(imgGuid, volGuid, size = volSize)

                #copy image (rhevm: create new template from VM)
                destIm, destVol = self._copyImage(imgGuid, snapUuid)
                time.sleep(1)

                for op in [IMAGE_COPY, IMAGE_MOVE]:
                    cpIm = self._moveImage(expSdUuid, destIm, op)
                    time.sleep(1)
                    #delete the copied image
                    self.assertVdsCallAndLog("Delete copied image from export domain",
                        "deleteImage", expSdUuid, self.spUuid, destIm)

                self.assertVdsCallAndLog("Delete image(template)", "deleteImage",
                    self.sdUuid, self.spUuid, destIm)
                self.assertVdsCallAndLog("Delete image(source VM)", "deleteImage",
                    self.sdUuid, self.spUuid, imgGuid)

        self.assertCallAndLog("RemoveExportDomainFromStoragePool", "removeDomainFromPool",
            self.spUuid, self.backupStorageType, expSdUuid, self.backupStorage)

    def dummyCleanup(self):
        """
        doing nothing beside of SP remove
        """
        self.doSPremove = True

#-----------------------------------------------------------------------------#
def suite():
    tests = {
        volumeTest : volumeTest.tests()
    }
    resSuite = ut.TestSuite()
    for testcase, methods in tests.iteritems():
        resSuite.addTests(map(testcase, methods))
    return resSuite

validateConfig = lambda cfg : vdst.validateSuiteConfig(suite(), cfg)
